/*Main program for the rccl emulator using  a Avr micro (atmega128 here)
model: Atmega128 16Mhz
 look at rccl.h for documentation

makefile makefile
avrdude

# ============================================================================
# Copyright (C) 2008 2009  2010Robert Spilleboudt
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#============================================================================
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <avr/io.h>
#include <avr/pgmspace.h> 
#include <avr/interrupt.h>
#include "io.h"
#include "rccl.h"
// needed before  theses include
#include "util/lcd.h"
#include <util/delay.h>
#include <avr/wdt.h>
//#include "stackmon/stackmon.h"


//declaration of the external data, common to all the RCCL functions
struct rccl_data rc; 
//memory for the stack monitor
/*
extern uint8_t _end; //end of variables
extern uint8_t __stack; //begin of the stack
extern uint8_t __brkval;
extern uint8_t __bss_end;
*/

#ifdef LIBRAM
struct rccl_lib lib; //the libs of the parameters are in RAM 
#endif
#ifdef LIBRAMROM
struct rccl_lib lib; //the libs of the parameters are in RAM 
#endif
int clock_calc_count; //how many calc cycles counted /period

#ifdef UART0
FILE*mypc ; /* file stream  */
/********  Debug messages on the serial terminal *********************
use a open of the stream
	mypc = fdevopen(uart_putchar,uart_getchar);
	and printf 
*/

/********* uart0 support for debug or PC link ************/
#define BAUD 19200
 void   uart_init (void)   {// initialisation UART channel 0 for Atmega128 - Bauds  8bits 2 stop
   #include <util/setbaud.h>
   UBRR0H = UBRRH_VALUE; //uart baud rate register
   UBRR0L = UBRRL_VALUE;
   #if USE_2X
   UCSR0A |= (1 << U2X); //Control and Status Register A 
   #else
   UCSR0A &= ~(1 << U2X);
   #endif
    UCSR0B = (1<<RXEN)|(1<<TXEN);   /* Enable receiver and transmitter 
	The USART Transmitter is enabled by setting the Transmit Enable (TXEN) bit in the UCSRB
	Register. When the Transmitter is enabled, the normal port operation of the TxD pin is overridden
	by the USART and given the function as the transmitter's serial output*/
    UCSR0C = (1<<USBS0)|(1<<UCSZ01)| (3<<UCSZ00);  /* USART Control and Status Register C  page 191-
	 
	 !!!!!!!!!!!!!!!why 3<<UCSZ00
	UCSZ00 and UCSZ01 for 8 bits
	USBS0=1  2 stop bits
*/   
   }

int uart_putchar(char c, FILE *stream) { //Send character c down the UART Tx, wait until tx holding register * is empty.
	loop_until_bit_is_set(UCSR0A, UDRE);
	UDR0 = c;
	return 0;
}
int uart_getchar(FILE *stream){  //get character c 
	loop_until_bit_is_set(UCSR0A, RXC);
	if (UCSR0A & _BV(FE))
		return _FDEV_EOF;
	if (UCSR0A & _BV(DOR))
		return _FDEV_ERR;
	return UDR0;
}
#endif
void rcloop(int io_ppm) {//the main loop with the transmitter functions
	//printf("AVR_rccl rcloop start******************\n");
	io_swread(); //read the position of the switches
	io_adc_all();  //read all analog inputs	
	io_butread5();//debounce the menu buttons and put a signal in the" state" variable
	io_butread100(); //process all the digital trims + - and accelerated move
	
	cli(); //test for  a clock tick  1/100 minute
		rc.L_tx[1]=rc.clock_tick; //tick  variable @HS
		rc.clock_tick=0;
	sei();
	
	clock_calc_count++;
	if (rc.L_tx[1]==1){
		rc.clock_calc= clock_calc_count;  // how many rcloop computing cycles during 1/100 minute
		//printf("AVR_rccl rc.clock_calc=%i\n",rc.clock_calc);
		clock_calc_count=0;
	}
	calc();
	if(strlen(rc.modelName)==0)
		_delay_ms(5); // rcloop is too short
	if (io_ppm ==0)
		io_ppm_load(); //transfer the calculated servo outputs to the PPM
	battery_monitor();
	#ifdef WDT
		wdt_reset( );
	#endif
	stackmon();	
}



int main(void) { //the main rccl program on the avr micro with the buttons and menu support
	//_______initialisation__________________________________________
	io_power(2);
	#ifdef WDT
		wdt_reset( );
		wdt_disable( );
	#endif	
	//initialize lcd
	lcd_init(LCD_DISP_ON);
	lcd_clrscr();
	lcd_puts_p(PSTR("Rccl  start \nGPL v2 licence"));
	rc.stackmin=0;
	#ifdef UART0
	uart_init();
	mypc = fdevopen(uart_putchar,uart_getchar);//creates the stdin and stdout
	stackmon();
	stackmargin(); //initial margin
	printf_P(PSTR("****** init uart \n") );
	if (ee_getchar(EE_POWER) == '$')
					printf_P(PSTR("Restart EE_POWER=$ \n") );

	//printf_P(PSTR("****** init stackmargin=%i \n"),stackmargin() );
	#endif

	//initialize the inputs and the PPM output
	io_init();
	param_init();
	//init model
	rc.modelName[0]='\0'; //no model is loaded
	if (sd_init() > 0){
		_delay_ms(5000);//there is a LCD message
		PORTC |= 1<<PC7;//beep
		_delay_ms(500);
		lcd_clrscr();
		
		io_power(0);//kill the regulator and the program

	}
	sd_restore_calibrate();
	
	sd_restore();
	sd_load_model();
	if (strlen(rc.modelName)>0){
		sd_restore_param();
	}
	if (ee_getchar(EE_POWER) == '$'){
		#ifdef UART0
		printf_P(PSTR("Restart EE_POWER=$ \n") );
		#endif
		rc.L_tx[0]=0; // NO reset chrono  to avoid the initial controls
	}
	else{
		rc.L_tx[0]=1; //  reset chrono 
	}
	io_power(1); //power on
	#ifdef UART0
	printf_P(PSTR("****** init AVR_rccl  @H1=  %i\n") ,rc.L_tx[0]);
	
	#endif
	rc.clock_time_start=0;
	clock_init();	
	rc.lcd_timer=500;// for the name of the model
	rc.beep =1; //short beep - release the start button 
	io_ppm_init(); //default initialization PPM18 - 9 channels 22.5msec 
	sei(); //  Enable global interrupts
	//uint8_t clock_calc_period;// 1 second
	clock_calc_count=0;
	//clock_calc_period=10;
	rc.clock_timer_sticks=rc.clock_time_start;
	rc.sticks=0;
	rc.io_maxspeed =0; //low speed digital trim flight mode
	#ifdef WDT
		wdt_enable(WDTO_500MS);
	#endif	
	stackmargin();
	#ifdef UART0
	printf_P(PSTR("****** start loop AVR_rccl  @H1=  %i\n") ,rc.L_tx[0]);
	
	#endif
	while(1){
		
		if ( io_getButton(MENU_SELECT) ==1){
			rc.io_maxspeed =1;
			menu_main(); // = first level of the menus
			rc.io_maxspeed =0;
		}
		rcloop(0); 
		param_button();//test if a button is pushed to assign a parameter to the digital trim
		lcd_fly_info(); //display the permanent message
		io_beep(); // beep during rc.beep
		}	
	return 0; //for  a happy compiler
}



/*stack monitor
the varable rc.stackmin  char*  is the lowest  stack pointer monitored by stackmon()
stackmon() is called in some critical or "deep" functions
stackmargin() returns  the difference between the minimum stack and a malloc, this is the free margin between he heap and the stack

eventually, the linker defines the up-border of the heap
,--defsym=__heap_end=0x800FA0
*/

void stackmon(){//called within a function to get an idea of th stack
	char x ; //variable allocated in the stack
	if (rc.stackmin ==0  || (int) &x < rc.stackmin )
		rc.stackmin=(int) &x;

}
int stackmargin(){//returns or print the free margin
	char *v;
	int w;
	v= malloc(32);
	if (v==NULL)
		return 0;
	w= (int)v;
	free(v);
	#ifdef UART0
		printf_P(PSTR("stackmargin :stackmin= %i malloc=%i margin=%i\n"),  rc.stackmin, w, (rc.stackmin-w)) ;
	#endif
	
	return (int) (rc.stackmin-w);
}	

